import tkinter as tk
from tkinter import filedialog, ttk, messagebox, scrolledtext
import json
import os
import subprocess
import threading
import sys
import sys
from sys import exit

# 获取当前执行目录（打包为EXE时也能正确工作）
def get_current_dir():
    if getattr(sys, 'frozen', False):
        # 当打包为EXE时，获取EXE所在目录
        return os.path.dirname(sys.executable)
    else:
        # 正常运行Python脚本时
        return os.path.dirname(os.path.abspath(__file__))

CURRENT_DIR = get_current_dir()

# 配置文件路径
CONFIG_FILE = os.path.join(CURRENT_DIR, "directory_cache.json")

# 初始化窗口
def init_window():
    root = tk.Tk()
    root.title("Luban配置工具")
    
    window_width = 800
    window_height = 600
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    center_x = int((screen_width - window_width) / 2)
    center_y = int((screen_height - window_height) / 2)
    root.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")
    root.resizable(True, True)
    
    for i in range(6):  
        root.columnconfigure(i, weight=1 if i == 2 else 0)
    
    return root

# 加载或创建缓存文件
def load_cache():
    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
        except json.JSONDecodeError:
            messagebox.showerror("错误", "配置文件损坏，将使用默认配置")
    return {
        "C数据目录": "",
        "C代码目录": "",
        "S数据目录": "",
        "S代码目录": ""
    }

# 保存缓存
def save_cache(cache):
    try:
        with open(CONFIG_FILE, "w", encoding="utf-8") as f:
            json.dump(cache, f, indent=2, ensure_ascii=False)
    except Exception as e:
        messagebox.showerror("错误", f"保存配置失败: {str(e)}")

# 目录选择函数
def select_directory(target_label, tips, cache):
    initial_dir = cache[tips] if cache[tips] else CURRENT_DIR
    selected_dir = filedialog.askdirectory(
        title=f"选择{tips}",
        initialdir=initial_dir
    )
    if selected_dir:
        display_path = selected_dir if len(selected_dir) < 50 else f"...{selected_dir[-47:]}"
        target_label.config(text=f"{tips}: {display_path}")
        cache[tips] = selected_dir
        save_cache(cache)
    else:
        target_label.config(text=f"未选择{tips}")

# 创建目录选择区块的函数
def create_directory_section(root, cur_row, label_text, cache):
    label = tk.Label(root, text=label_text, wraplength=400)
    label.grid(row=cur_row, column=0, padx=(10, 10), pady=(10, 0), sticky="w")
    
    if cache[label_text]:
        display_path = cache[label_text] if len(cache[label_text]) < 50 else f"...{cache[label_text][-47:]}"
        label.config(text=f"{label_text}: {display_path}")
    else:
        label.config(text=f"未选择{label_text}")
    
    button = ttk.Button(
        root,
        text="选择目录",
        command=lambda: select_directory(label, label_text, cache)
    )
    button.grid(row=cur_row, column=5, padx=(0, 20), pady=(10, 0), sticky="e")
    
    cur_row += 1
    
    underline = ttk.Separator(root, orient="horizontal")
    underline.grid(
        row=cur_row, 
        column=0,          
        columnspan=6,       
        sticky="ew",      
        pady=5
    )
    
    return cur_row + 1

# 执行批处理文件并显示输出
def run_batch_file(bat_path, output_text):
    def execute():
        if not os.path.exists(bat_path):
            output_text.insert(tk.END, f"错误: 批处理文件不存在 - {bat_path}\n")
            output_text.insert(tk.END, "----------------------------------------\n")
            output_text.see(tk.END)
            return

        try:
            output_text.insert(tk.END, f"正在执行批处理文件: {bat_path}\n")
            output_text.insert(tk.END, "请等待执行完成...\n")
            output_text.see(tk.END)

            if sys.platform.startswith('win32'):
                result = subprocess.run(
                    f'start /wait cmd /c "{bat_path}"',
                    cwd=CURRENT_DIR,
                    shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    encoding='gbk'
                )
            else:
                result = subprocess.run(
                    bat_path,
                    cwd=CURRENT_DIR,
                    shell=True,
                    check=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.STDOUT,
                    encoding='utf-8'
                )

            if result.returncode == 0:
                output_text.insert(tk.END, f"批处理执行成功 (返回代码: {result.returncode})\n")
                if result.stdout:
                    output_text.insert(tk.END, f"输出:\n{result.stdout}\n")
            else:
                output_text.insert(tk.END, f"批处理执行失败 (返回代码: {result.returncode})\n")
                if result.stdout:
                    output_text.insert(tk.END, f"错误输出:\n{result.stdout}\n")

        except subprocess.CalledProcessError as e:
            output_text.insert(tk.END, f"批处理执行失败 (代码 {e.returncode}):\n{e.stdout}\n")
        except Exception as e:
            output_text.insert(tk.END, f"执行批处理时发生错误:\n{str(e)}\n")
        finally:
            output_text.insert(tk.END, "----------------------------------------\n")
            output_text.see(tk.END)
    
    threading.Thread(target=execute, daemon=True).start()

# 生成批处理文件
def generate_batch_file(data_path, code_path, target, code_type, data_type, bat_filename, output_text):
    if not data_path or data_path.strip() == "":
        messagebox.showerror("错误", f"数据目录为空，请重新选择")
        return None
        
    if not code_path or code_path.strip() == "":
        messagebox.showerror("错误", f"代码目录为空，请重新选择")
        return None
        
    if not os.path.isdir(data_path):
        messagebox.showerror("错误", f"数据目录不存在: {data_path}")
        return None
        
    if not os.path.isdir(code_path):
        messagebox.showerror("错误", f"代码目录不存在: {code_path}")
        return None

    try:
        bat_path = os.path.join(CURRENT_DIR, f"{bat_filename}.bat")
        output_text.insert(tk.END, f"生成批处理文件: {bat_path}\n")
        
        formatted_data_path = data_path.replace('/', '\\')
        formatted_code_path = code_path.replace('/', '\\')
        
        bat_content = f"""@echo off
set "BAT_DIR=%~dp0"
echo 批处理文件位置: %BAT_DIR%

set "WORKSPACE=%BAT_DIR%.."
echo 计算得到的WORKSPACE: %WORKSPACE%

set "LUBAN_DLL=%WORKSPACE%\Tools\Luban\Luban.dll"
echo 计算得到的Luban.dll路径: %LUBAN_DLL%

set "CONF_ROOT=."
echo 目标: {target}
echo 代码类型: {code_type}
echo 数据类型: {data_type}
echo 代码输出目录: {formatted_code_path}
echo 数据输出目录: {formatted_data_path}

if not exist "%LUBAN_DLL%" (
    echo 错误: 未找到Luban.dll
    echo 预期路径: %LUBAN_DLL%
    echo 请检查Tools\Luban目录下是否存在Luban.dll
    pause
    exit /b 1
)

if not exist "%CONF_ROOT%\luban.conf" (
    echo 错误: 未找到配置文件 - %CONF_ROOT%\luban.conf
    pause
    exit /b 1
)

dotnet "%LUBAN_DLL%" ^
    -t {target} ^
    -c {code_type} ^
    -d {data_type} ^
    --conf "%CONF_ROOT%\luban.conf" ^
    -x outputCodeDir="{formatted_code_path}" ^
    -x outputDataDir="{formatted_data_path}"

if %errorlevel% equ 0 (
    echo 命令执行成功
) else (
    echo 命令执行失败，错误代码: %errorlevel%
)

pause
"""

        with open(bat_path, 'w', encoding='gbk') as f:
            f.write(bat_content)
        
        return bat_path
    except Exception as e:
        error_msg = f"生成批处理文件失败: {str(e)}"
        messagebox.showerror("错误", error_msg)
        output_text.insert(tk.END, error_msg + "\n")
        return None

# 运行处理函数
def process_run(comboboxes, directory_cache, output_text):
    output_text.delete(1.0, tk.END)
    output_text.insert(tk.END, f"当前工具目录: {CURRENT_DIR}\n")
    output_text.insert(tk.END, "开始处理...\n")
    output_text.insert(tk.END, "----------------------------------------\n")
    
    target = comboboxes["target"].get()
    code_type = comboboxes["code_type"].get()
    data_type = comboboxes["data_type"].get()
    
    success_count = 0
    bat_files = []
    
    if target in ["all", "client"]:
        c_data_path = directory_cache.get("C数据目录", "")
        c_code_path = directory_cache.get("C代码目录", "")
        
        if not c_data_path:
            messagebox.showerror("错误", "C数据目录未配置")
        elif not c_code_path:
            messagebox.showerror("错误", "C代码目录未配置")
        else:
            bat_path = generate_batch_file(
                c_data_path,
                c_code_path,
                "client",
                code_type,
                data_type,
                "gen_client",
                output_text
            )
            if bat_path:
                success_count += 1
                bat_files.append(bat_path)
    
    if target in ["all", "server"]:
        s_data_path = directory_cache.get("S数据目录", "")
        s_code_path = directory_cache.get("S代码目录", "")
        
        if not s_data_path:
            messagebox.showerror("错误", "S数据目录未配置")
        elif not s_code_path:
            messagebox.showerror("错误", "S代码目录未配置")
        else:
            bat_path = generate_batch_file(
                s_data_path,
                s_code_path,
                "server",
                code_type,
                data_type,
                "gen_server",
                output_text
            )
            if bat_path:
                success_count += 1
                bat_files.append(bat_path)
    
    if success_count > 0:
        messagebox.showinfo("成功", f"已成功生成 {success_count} 个批处理文件，即将执行...")
        for bat_path in bat_files:
            run_batch_file(bat_path, output_text)
    else:
        output_text.insert(tk.END, "未生成任何批处理文件，无法执行\n")

def main():
    try:
        root = init_window()
        
        info_label = tk.Label(root, text=f"当前工具目录: {CURRENT_DIR}", fg="gray", wraplength=700)
        info_label.grid(row=0, column=0, columnspan=6, padx=10, pady=(5, 10), sticky="w")
        
        directory_cache = load_cache()
        
        cur_row = 1
        directory_labels = ["C数据目录", "C代码目录", "S数据目录", "S代码目录"]
        for label_text in directory_labels:
            cur_row = create_directory_section(root, cur_row, label_text, directory_cache)
        
        TARGET_OPTIONS = ["all", "client", "server"]
        CODE_TYPE_OPTIONS = ["cs-simple-json", "cs-dotnet-json"]
        DATA_TYPE_OPTIONS = ["json"]

        options = [
            ("分组目标", TARGET_OPTIONS, "target"),
            ("代码类型", CODE_TYPE_OPTIONS, "code_type"),
            ("数据类型", DATA_TYPE_OPTIONS, "data_type")
        ]

        combobox_dict = {}

        for text, values, key in options:
            label = tk.Label(root, text=text, wraplength=350)
            label.grid(row=cur_row, column=0, padx=(10, 10), pady=(10, 0), sticky="w")
            
            combobox = ttk.Combobox(root, values=values, state="readonly", width=20) 
            combobox.grid(row=cur_row, column=3, padx=(10, 10), pady=(10, 0), sticky="w")
            combobox.current(0)
            combobox_dict[key] = combobox
            
            cur_row += 1

            underline = ttk.Separator(root, orient="horizontal")
            underline.grid(
                row=cur_row, 
                column=0,          
                columnspan=6,       
                sticky="ew",      
                pady=5
            )
            cur_row += 1

        output_label = tk.Label(root, text="执行输出:", wraplength=350)
        output_label.grid(row=cur_row, column=0, padx=(10, 10), pady=(10, 0), sticky="w")
        cur_row += 1
        
        output_text = scrolledtext.ScrolledText(root, wrap=tk.WORD, height=8)
        output_text.grid(row=cur_row, column=0, columnspan=6, padx=10, pady=(0, 10), sticky="nsew")
        cur_row += 1

        root.rowconfigure(cur_row - 1, weight=1)

        button_frame = ttk.Frame(root)
        button_frame.grid(row=cur_row, column=0, columnspan=6, pady=10)
        
        button_create = ttk.Button(
            button_frame,
            text="生成并执行",
            command=lambda: process_run(combobox_dict, directory_cache, output_text),
            width=15
        )
        button_create.pack(side="right", padx=(0, 20))

        button_manual = ttk.Button(
            button_frame,
            text="打开工具目录",
            command=lambda: os.startfile(CURRENT_DIR),
            width=15
        )
        button_manual.pack(side="right", padx=(10, 0))

        root.mainloop()
    except Exception as e:
        messagebox.showerror("致命错误", f"程序发生错误: {str(e)}")
        exit(1)

if __name__ == "__main__":
    main()
    